路由属性及方法

这里的history并不是window.history,而是React-Router的history。

这里以BrowserRouter举例,以Router指代BrowserRouter。

在ReactRouter中,路由配置都是包含在Router组件中的。如果APP的组件需要接收路由信息,那么都应该放在Route里。同一个APP不应该包含多个Router,看下例:

import React from 'react';
import ReactDOM from 'react-dom';
import {Link,BrowserRouter as Router,Route} from 'react-router-dom';

class MyApp extends React.Component{
    render(){
       return(
            <div>
                <Router>
                    <div>
                        <Link to="/" children="home" />
                        <Link to="/about" children="about" />
                        <Route exact path="/" render={(props)=>{return <h1>home</h1>}} />
                        <Route exact path="/about" render={(props)=>{return <h1>about</h1>}} />
                    </div>
                </Router>
                <Router>
                    <div>
                        <Link to="/" children="home" />
                        <Link to="/about" children="about" />
                        <Route exact path="/" render={(props)=>{return <h1>home</h1>}} />
                        <Route exact path="/about" render={(props)=>{return <h1>about</h1>}} />
                    </div>
                </Router>
            </div>
            ); 
        }

}
ReactDOM.render(<MyApp/>,document.querySelector("#root"));

上下俩个Router都配置了相同的路由,但并不会互相同步,彼此是隔离的,这也符合React的封装性。

Router管理路由,路由属性会由Route组件传递给每一个子组件。所以上例,切换路由Link组件是不会再次渲染的,也无法获取到路由信息。


路由属性

完整的例子:

import React from 'react';
import ReactDOM from 'react-dom';
import {Link,BrowserRouter as Router,Route,Switch} from 'react-router-dom';

class Home extends React.Component{
    render(){
        return(
            <div>
                <h1>Home</h1>
                <section>{JSON.stringify(this.props.history)}</section>
            </div>
        )
    }
}
class About extends React.Component{
    render(){
        return(
            <div>
                <h1>Home</h1>
                <section>{JSON.stringify(this.props.history)}</section>
            </div>
        )
    }
}
class RouteLink extends React.Component{
    render(){
        return(
            <Route exact path={this.props.to} children={(props)=>{
                return(
                    <div style={{color:props.match?'red':''}}>
                        <Link to={this.props.to} children={this.props.children}/>
                    </div>
                );
            }} />
        );
    }
}


class MyApp extends React.Component{
    render(){
        return(
            <Router>
                <div>
                    <RouteLink to="/" children="home" />
                    <RouteLink to="/about" children="about" />
                    <Route exact path="/" component={Home} />
                    <Route exact path="/about" component={About} />
                </div>
            </Router>
        );
    }
}
ReactDOM.render(<MyApp/>,document.querySelector("#root"));

定义了俩个路由Home和About,封装了Link组件RouteLink。注意RouteLink组件中以Route包装了Link,这里的Route因为要一直渲染,所以组件方法要用children而不能是component、render;同时精确匹配要配置exact。

history属性

挂载在Route子组件的props上。

参考上例,可以看到history包含如下属性:

  • length:历史栈长度,最大为50(原生js就是最大50)
  • action:类型,入栈就是PUSH,出栈就是POP,替换就是REPLACE
  • location:location属性,包含5个熟悉:
    • hash:hash
    • key:默认6位随机数字字母组合字符串(浏览器重装不会生成该属性,只有路由跳转才会生成)
    • pathname:访问路径
    • search:包含?的查询字符串
    • state:默认undefined,可以在跳转配置该参数,传递一个对象,用来辅助传递数据

location属性

挂载在Route子组件的props上。

等价于history的location属性。

match属性

挂载在Route子组件的props上。

如果当前路由不匹配,那么该属性值为null,所以你可以看到上例,添加背景色就是以此来判断的。

match属性包含如下属性:

  • isExact:是否精确匹配
  • path:Route配置path
  • url:访问路径
  • params:参数对象(路由是/:id,那么值就是{id:'xxx'})

history.push()

该方法会向历史栈添加一条记录,同时跳转到指定路由。

第一种用法:第一个参数为url,第二个参数为object用来设置state

第二种用法:第一个参数为object,配置具体的pathname、hash、search、state等

this.props.history.push("/about#hash?name=geralt",{from:"/"})
//等价于
this.props.history.push({
    pathname:"/about",
    hash:"#hash",
    search:"?name=geralt",
    state:{
        from:"/"
    }
})

history.replace()

该方法会用新的路由代替当前路由,并跳转。

用法同push()一样。

history.go(n)、history.goBack()、history.goForward()

go方法传递一个整数数字,正数代表前进,负数代表后退,不传或传0代表刷新。

go(1)
//等价于
goForward()

go(-1)
//等价于
goBack()

这三个方法都会导致页面重载。

history.block()

该方法实际上就是Prompt组件的函数式实现。在Prompt组件中,message可以是字符串也可以是函数,在block方法中也一样,可以传递一个字符串,也可以传递一个函数。

同时该方法会返回一个新方法,调用该方法将销毁window.confirm()注册的提示。

//我们改写之前的Home组件
class Home extends React.Component{
    componentDidMount(){
        this.unblock = this.props.history.block((nextLocation,action)=>{
            return "确认离开首页???"
        })
    }
    componentWillUnmount(){
        this.unblock();
    }
    render(){
        return(
            <div>
                <h1>Home</h1>
                <section>{JSON.stringify(this.props.history)}</section>
            </div>
        )
    }
}

这样,当离开Home组件匹配的路由前就会出现confirm对话框,而离开"/about"路由则不会出现。

为了避免重复注册,要在卸载周期函数中销毁实例。

block方法如果传递函数,则第一个参数为下一个路由的location属性,第二个参数为action类型(PUSH、POP、REPLACE三者之一)。函数返回true,表示不提示直接跳转;函数返回false,表示不提示阻止跳转。

可以看出来,该方法要比Prompt组件灵活很多。

history.listen(func)

调用该方法将注册一个监听函数,当路由发生变化则触发该函数。

同block()方法一样,是全局的,即"/"路由组件注册了,那么在"/about"路由内跳转也会触发。同时调用该方法也会返回一个方法,执行该方法即可销毁注册的监听函数。

class Home extends React.Component{
    componentDidMount(){
        this.unlisten = this.props.history.listen((nextLocation)=>{
            console.log(nextLocation);
        })
    }
    componentWillUnmount(){
        this.unlisten();
    }
    render(){
        return(
            <div>
                <h1>Home</h1>
                <section>{JSON.stringify(this.props.history)}</section>
            </div>
        )
    }
}

history.createHref(func)

该方法也传递一个函数,函数接收一个参数location。该方法本身会返回当前路由的pathname。

官方文档并没有介绍该方法,所以暂时无法得知具体的使用场景。

results matching ""

    No results matching ""